#ifndef __SELECT_SERVER__
#define __SELECT_SERVER__
#include "Socket.hpp"
#include <vector>
#include <poll.h>

static const uint16_t DEFAULT_PORT       = 9000;         // 默认端口号
static const int      DEFAULT_FD         = -1;           // 默认FD是-1
static const int      DEFAULT_ARRAY_SIZE = 1024;         // 默认的长度是1024
static const short    DEFAULT_EVENT      = 0;            // 默认事件 
extern Log            log;

class PollServer
{
public:
    PollServer(const uint16_t& port = DEFAULT_PORT) : _port(port)
    {
        // 初始化_fds
        _fds.resize(DEFAULT_ARRAY_SIZE);
        for(int i = 0; i < DEFAULT_ARRAY_SIZE; ++i) {
            _fds[i].fd      = DEFAULT_FD;
            _fds[i].events  = DEFAULT_EVENT;
            _fds[i].revents = DEFAULT_EVENT;
        }
    }  

    ~PollServer()
    {

    }

    void initServer()
    {
        _listenSocket.Socket();
        // 允许套接字绑定到一个处于 TIME_WAIT 状态的端口
        int reuse = 1;
        setsockopt(_listenSocket.GetFd(), SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &reuse, sizeof(reuse));
        _listenSocket.Bind(_port);
        _listenSocket.Listen();
    }

    void startServer()
    {
        /* accept会阻塞等待客户端的连接，一次只能等一个，
        但我们这是一个实现多路转接的服务器，需要一次等待多个客户端，所以要先select
        每次有一个新连接到来，等价于读事件就绪。*/ 

        int listenSocket = _listenSocket.GetFd();
        _fds[0].fd       = listenSocket;     // 第一个元素是监听套接字的fd
        _fds[0].events   = POLLIN;           // 关心监听套接字的读事件
        int timeout      = -1;               // 设置为负值，用于阻塞等待
        for(; ;) {
            // // 进行poll
             int n = poll(_fds.data(), _fds.size(), timeout);
             // 判断n，进行不同的操作
             switch (n)
             {
             case 0:
                 log(INFO, "Time out!\n");
                 break;
             case -1:
                 log(ERROR, "Select error!\n");
                break;
             default:
                // 有事件就绪了。如果事件就绪，上层不处理，select就会一直通知用户，让其处理
                // 一个事实是：如果select告诉用户数据已经就绪了，接下来，用户读取fd时不会被阻塞。      
                log(INFO, "Get a new link!\n");
                handlerEvent();    
                break;
            }
            // int n = poll(_fds.data(), _fds.size(), timeout);
            // switch (n)
            // {
            // case 0:
            //     cout << "time out... " << endl;
            //     break;
            // case -1:
            //     cerr << "poll error" << endl;
            //     break;
            // default:
            //     // 有事件就绪了，TODO
            //     cout << "get a new link!!!!!" << endl;
            //     handlerEvent();     
            //     break;  
            // }
        }
    }

    bool handlerEvent()
    {   
        // 这个循环处理_fds中所有已经就绪的事件，包括监听套接字创建连接和读事件
        for(int i = 0; i < _fds.size(); ++i) {
            int fd            = _fds[i].fd;
            int rEvent        = _fds[i].revents;
            int listenSocket  = _listenSocket.GetFd();
            if(fd == DEFAULT_FD)
                continue;

            if(fd == listenSocket) {
                if(rEvent & POLLIN) {
                    // 是监听套接字并且有读事件，证明是要创建连接
                    acceptLink();
                } else {    
                    // 是监听套接字并且没有读事件
                    log(INFO, "%d是监听套接字, 没有读事件\n", fd);
                }
            } else if(fd != listenSocket) {
                if(rEvent & POLLIN) {
                    // 不是监听套接字，但是有读事件，证明可以read
                    readData(fd, i);
                } else {
                    // 不是监听套接字, 没有读事件
                    log(INFO, "%d不是监听套接字, 没有读事件\n", fd);
                }   
            } else {
                log(INFO, "其它情况...\n");
            }
        }

        // for (int i = 0; i < _fds.size(); i++) // 这是第三个循环
        // {
        //     int fd = _fds[i].fd;
        //     if (fd == DEFAULT_FD)
        //         continue;

        //     if (_fds[i].revents & POLLIN)
        //     {
        //         if (fd == _listenSocket.GetFd())
        //         {
        //             acceptLink(); // 连接管理器
        //         }
        //         else // non listenfd
        //         {
        //             readData(fd, i);
        //         }
        //     }
        // }

        return true;
    }
private:
    void printFdArray()
    {
        printf("fdArray: ");
        for(int i = 0; i < _fds.size(); ++i) {
            if(_fds[i].fd == DEFAULT_FD)
                break;
            printf("%d ", _fds[i].fd);
        }
        printf("\n");
    }
    
    // 创建连接
    void acceptLink()
    {
        std::string ip;
        uint16_t port;
        // 由于此事件已经就绪了，所以accept不会被阻塞，这样就省去了等的过程
        int socket = _listenSocket.Accept(&ip, &port);      
        if(socket < 0)     return;
        log(INFO, "Accept success, ip: %s, port: %d\n", ip.c_str(), port);

        // 该循环会在_fdArray找一个空闲的位置，把连接套接字放进去
        int i          = 1;
        size_t fdSize  = _fds.size();
        for(; i < fdSize; ++i) {
            if(_fds[i].fd != DEFAULT_FD)    
                continue;
            else   
                break;
        }
        if(i == fdSize) {
            // 扩容，2倍
            log(WARNING, "Fd array has no size! In the process of expansion...\n");
            _fds.resize(fdSize * 2);
            close(socket);
            return;
        } else {
            // 将该连接套接字添加进_fds中
            _fds[i].fd      = socket;
            _fds[i].events  = POLLIN;
            _fds[i].revents = DEFAULT_EVENT;    // 这个由OS填写
            printFdArray();
        }
    }

    // 读数据
    void readData(int fd, int i)
    {
        char buff[DEFAULT_ARRAY_SIZE];
        ssize_t n = read(fd, buff, sizeof buff - 1);
        if(n > 0) {
            buff[n] = 0;
            log(INFO, "echo: %s", buff);
        } else if(n == 0) {
            log(INFO, "read done\n");
            close(fd);
            _fds[i].fd = DEFAULT_FD;       // 将其从数组中移除
        } else {
            log(WARNING, "read error! errno: %d, why: %s\n", errno, strerror(errno));
            close(fd);
            _fds[i].fd = DEFAULT_FD;
        }
    }

    Sock _listenSocket;
    uint16_t _port;
    // struct pollfd* _fds;
    std::vector<pollfd> _fds;
};

#endif